package com.itany.shop.servlet;

import com.itany.shop.exception.FileUploadErrorException;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Author:shixiaojun@itany.com
 * Date:2022/4/20 13:30
 * Version:1.0-SNAPSHOT
 * Description:
 */
@WebServlet("*.file")
public class FileServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String path = request.getServletPath();
        if ("/upload.file".equals(path)) {
            upload(request, response);
        }
        if ("/download.file".equals(path)) {
            download(request, response);
        }
        if ("/download2.file".equals(path)) {
            download2(request, response);
        }
    }

    public void download(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, FileUploadErrorException {
        // 找到资源所在位置
        String path = getServletContext().getRealPath("/js/jquery-3.4.1.min.js");

        // 使用IO进行文件的复制操作
        FileInputStream in = null;
        OutputStream out = null;
        try {
            in = new FileInputStream(path);
            out = response.getOutputStream();

//            byte[] b = new byte[10240];
//            int i;
//            while((i = in.read(b)) != -1){
//                out.write(b,0,i);
//            }

            // 文件下载时遵循MIME协议
            // 通过该协议指定附件的方式
            // 如果不指定附件的方式,会由浏览器默认选择方式进行操作
            // 响应时,在消息头中指定一个content-disposition属性的值
            // 该属性值有两种
            // 1.attachment:以附件的方式进行下载
            // 2.inline:在线打开
//            response.setHeader("content-disposition","attachment");
            // 此时能够通过附件的方式进行下载
            // 但是下载的资源并不是源文件名,而是以当前请求命名的
            // 可以指定下载的资源名
            // 指定附件方式的时候可以同时指定资源名
            // 多个值之前以分号分隔
//            response.setHeader("content-disposition","attachment;filename=jquery-3.4.1.min.js");
            // 对于资源名,只识别非中文以及ISO-8859-1的中文
//            response.setHeader("content-disposition","attachment;filename=中文名");
            // 因此,如果存在中文,则需要将其字符集转换为ISO-8859-1
            response.setHeader("content-disposition",new String("attachment;filename=中文名".getBytes(),"ISO-8859-1"));

            byte[] b = new byte[10240];
            int i;
            while((i = in.read(b)) != -1){
                out.write(b,0,i);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

    }

    public void download2(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, FileUploadErrorException {
        // 找到资源所在位置
        String path = getServletContext().getRealPath("/lib/jstl-1.2.jar");

        // 使用IO进行文件的复制操作
        FileInputStream in = null;
        OutputStream out = null;
        try {
            in = new FileInputStream(path);
            out = response.getOutputStream();

//            byte[] b = new byte[10240];
//            int i;
//            while((i = in.read(b)) != -1){
//                out.write(b,0,i);
//            }

            // 文件下载时遵循MIME协议
            // 通过该协议指定附件的方式
            // 如果不指定附件的方式,会由浏览器默认选择方式进行操作
            // 响应时,在消息头中指定一个content-disposition属性的值
            // 该属性值有两种
            // 1.attachment:以附件的方式进行下载
            // 2.inline:在线打开
            response.setHeader("content-disposition","attachment");

            byte[] b = new byte[10240];
            int i;
            while((i = in.read(b)) != -1){
                out.write(b,0,i);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

    }

    public void upload(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, FileUploadErrorException {
        // 该方式只适用于获取表单数据,而不适用于二进制流数据
//        String username = request.getParameter("username");
//        String file = request.getParameter("file");
//        System.out.println("username:"+username);
//        System.out.println("file:"+file);

        // 获取请求中的二进制流数据
//        InputStream in = request.getInputStream();
//        byte[] b = new byte[1024];
//        int i = in.read(b);
//        System.out.println(new String(b,0,i));
        // 此时,二进制流数据已经获取到了
        // 需要对这些数据进行解析
        // 而这些数据的解析比较麻烦
        // 因此,可以使用apache提供的第三方工具进行解析
        // 该工具只是做的解析操作


        // 使用commons-fileupload对二进制流数据进行解析
        // 且在该依赖所依赖的commons-io中封装了io的相关方法
        // 可以直接调用对应的方法进行对应的io操作

        // 获取解析工厂
        DiskFileItemFactory factory = new DiskFileItemFactory();

        // 使用该解析工厂生成与之对应的解析器
        ServletFileUpload upload = new ServletFileUpload(factory);

        try {
            // 开始解析
            // 解析后返回的是一个FileItem集合
            // 每一个FileItem对应的是表单数据中的一个元素
            List<FileItem> fileItems = upload.parseRequest(request);
//            for(FileItem fileItem : fileItems){
//                // 通过请求数据中key的值判断当前是哪一个元素
//                // fileItem.getFieldName()用于获取请求数据中对应的key
//                if("username".equals(fileItem.getFieldName())){
//                    System.out.println("这是用户名");
                    // 如果是非文件,getString()用于获取对应的数据
                    // 可以通过传递参数指定编码,例如:getString("UTF-8");
//                    String username = fileItem.getString();
//                    System.out.println("用户名:"+username);
//                }
//                if("file".equals(fileItem.getFieldName())){
//                    System.out.println("这是文件");
//                    // 如果是文件,getString()用于获取文件的内容
//                    String file = fileItem.getString();
//                    System.out.println("文件内容:"+file);
//                }
//            }
            // 默认返回的是List集合
            // 但是List集合在使用时需要判断当前的表单元素是谁
            // 有几个表单元素就需要判断几次
            // 这种方式相对来说比较麻烦
            // 因此,可以考虑将其转换成Map集合来实现
            Map<String, FileItem> items = new HashMap<>();
            for (FileItem fileItem : fileItems) {
                // 遍历对应的List集合
                // 每遍历一个元素,向Map集合添加一个元素
                // 添加时,key为getFieldName(),value是当前对象
                items.put(fileItem.getFieldName(), fileItem);
            }

            // 获取用户名
            System.out.println("用户名:" + items.get("username").getString("UTF-8"));
            System.out.println("文件内容:" + items.get("file").getString());
            System.out.println("文件名:" + items.get("file").getName());

            FileItem file = items.get("file");

            // 某些时候,在上传的时候需要对文件进行格式限定
            // 不同格式的文件其后缀名一定不同
            // 可以通过后缀名判断格式
            // 假设此处不允许上传jsp文件
            // 取出当前的文件名
            String fileName = file.getName();
            if(fileName.endsWith(".jsp")){
                request.setAttribute("uploadMsg","对不起,上传失败,不允许上传jsp文件");
                request.getRequestDispatcher("/jsp/upload.jsp").forward(request,response);
                return;
            }

            // 将文件上传到服务器中
            // 获取服务器地址
            // 根目录
            // String path = getServletContext().getRealPath("");
            // 在服务器根目录中创建一个子目录upload
            // 每天在子目录upload中再次创建一个子目录
            // 该目录以当前日期的年月日进行命名,例如:20220420
            // 在年月日命名的子目录中进行文件的上传
            String path = getServletContext().getRealPath("/upload/" + new SimpleDateFormat("yyyyMMdd").format(new Date()));
            System.out.println("上传的文件的路径:" + path);

            // 创建目录
            File f = new File(path);
            f.mkdirs();

            // 在上传时,不同用户上传的文件可能会出现重名的
            // 如果出现重名会覆盖原有内容
            // 为了避免这个情况,我们可以每次为上传的文件取一个别名,这个别名不能重复
            // 为文件加一个毫秒数的后缀
            fileName = fileName.substring(0,fileName.lastIndexOf("."))+"-"+new Date().getTime()+fileName.substring(fileName.lastIndexOf("."));

            // 将文件上传到对应的目录中
            // FileItem对象中封装了上传的方法
            // FileItem中封装了file.write(targetFile)方法用于上传
            // 表示将源文件file复制到目标文件targetFile中
            file.write(new File(path,fileName));
        } catch (Exception e) {
            throw new FileUploadErrorException("文件上传失败");
        }

    }

}
